class h_syllabus {

	async addStuCourseData(stuId, stuSyllabus, courseTime, majorId, classId) {
		return await this._addStuCourseData(stuId, stuSyllabus, courseTime, majorId, classId)
	}

	async addTchCourseData(tchId, tchSyllabus, courseTime, courseClasses) {
		return await this._addTchCourseData(tchId, tchSyllabus, courseTime, courseClasses)
	}

	async addStuSyllabusData(stuId, courseTime, courseData) {
		return await this._addStuSyllabusData(stuId, courseTime, courseData)
	}

	async getStuSyllabusData(stuId, courseYear, courseTerm) {
		const result = await this.mysql.cxxy.select({
			table: 'student_syllabus',
			field: 'syllabusData',
			where: 'syllabusStuId=$syllabusStuId && syllabusYear=$syllabusYear && syllabusTerm=$syllabusTerm',
			data: {
				syllabusStuId: stuId,
				syllabusYear: courseYear,
				syllabusTerm: courseTerm
			}
		})
		if(result.length == 0)
			return null
		await this.redis.cache.set(`syllabus@${courseYear}@${courseTerm}@student@${stuId}`, result[0].syllabusData, this.config.frame.sessionExpires.syllabus)
		return result[0].syllabusData
	}

	async getStuSyllabusCache(stuId, courseYear, courseTerm) {
		const syllabusData = await this.redis.cache.get(`syllabus@${courseYear}@${courseTerm}@student@${stuId}`)
		if(syllabusData)
			return syllabusData
		return false
	}

	async getCorrelSyllabusData(stuId) {
		const correlData = await this.mysql.cxxy.select({
			table: [
				'syllabus_correl c',
				'student s'
			],
			field: [
				'correlId',
				'correlOtherStuId',
				'stuName as correlOtherStuName',
				'correlDate'
			],
			where: 'c.correlStuId=$correlStuId and c.correlOtherStuId=s.stuId',
			data: {
				correlStuId: stuId
			},
			decrypt: [
				'correlOtherStuName'
			]
		})
		return correlData
	}

	async bindCorrelSyllabus(correlStuId, user) {
		console.log('发送消息', correlStuId, user)
		if(!correlStuId)
			return this.reject('-2046', 'correl student not found')
		const {userId} = user
		const {stuId, stuName} = await this.hd.student.getStuDataByUserId(userId, 'stuId', 'stuName')
		if(stuId == correlStuId)
			return this.reject('-2061', 'could not correl onerself syllabus')
		if(await this._checkCorrelSyllabus(stuId, correlStuId))
			return this.reject('-2059', 'syllabus is already correl')
		await this.hd.sys.pushStuMessage(user.userId, correlStuId, 'correlSyllabus', {
			stuId, //绑定者学号
			stuName, //绑定者学生名字
			correlStuId, //被绑定者学号
		})
		return 1
	}

	async untieCorrelSyllabus(correlId) {
		const correlData = await this.mysql.cxxy.select({
			table: 'syllabus_correl',
			field: [
				'correlStuId',
				'correlOtherStuId'
			],
			where: 'correlId=$correlId',
			data: {
				correlId
			}
		})
		if(!correlData[0])
			return this.reject('-2060', 'syllabus correl relation not found')
		const {correlStuId, correlOtherStuId} = correlData[0]
		//通知被解绑的学生
		const {stuName} = await this.hd.student.getStuDataByStuId(correlStuId, 'stuName')
		await this.hd.sys.pushStuMessage(correlStuId, correlOtherStuId, 'untieCorrelSyllabus', {
			stuName
		})
		await this.mysql.cxxy.delete({
			table: 'syllabus_correl',
			where: 'correlId=$correlId or (correlOtherStuId=$correlStuId and correlStuId=$correlOtherStuId)',
			data: {
				correlId,
				correlStuId,
				correlOtherStuId
			}
		})
		return 1
	}

	async resolveCorrelSyllabus(msgId, user) {
		const data = await this.hd.sys.getMsgDataByMsgId(msgId, 'msgSender', 'msgData', 'msgStatus')
		if(!data)
			return this.reject('-2054', 'message not found')
		let {msgSender, msgData, msgStatus} = data
		if(msgStatus !== 0)
			return this.reject('-2057', msgStatus)
		const {stuId, correlStuId} = msgData
		if(await this._checkCorrelSyllabus(stuId, correlStuId))
			return this.reject('-2059', 'syllabus is already correl')
		//获取自己的学生姓名
		const {userId} = user
		const {stuName} = await this.hd.student.getStuDataByUserId(userId, 'stuName')
		await this.mysql.cxxy.trans()
		//添加关联关系
		await this._addCorrelSyllabus(stuId, correlStuId)
		await this.hd.sys.pushUserMessage(userId, msgSender, 'resolveCorrelSyllabus', {
			correlStuId, //被绑定者学号
			correlStuName: stuName //被绑定者学生名字
		})
		await this.hd.sys.updateMsgStatus(msgId, 1)
		await this.mysql.cxxy.cmt()
		return 1
	}

	async rejectCorrelSyllabus(msgId, user) {
		const data = await this.hd.sys.getMsgDataByMsgId(msgId, 'msgSender', 'msgReceiver', 'msgStatus', 'msgData')
		if(!data)
			return this.reject('-2054', 'message not found')
		const {msgSender, msgReceiver, msgStatus, msgData} = data
		if(msgStatus !== 0)
			return this.reject('-2057', msgStatus)
		const {userId} = user
		await this.hd.sys.pushUserMessage(userId, msgSender, 'rejectCorrelSyllabus', {
			stuId: msgData.correlStuId //被绑定者学号
		})
		await this.hd.sys.updateMsgStatus(msgId, 2)
		return 1
	}

	async checkCorrelSyllabus(stuId, correlStuId) {
		return await this._checkCorrelSyllabus(stuId, correlStuId)
	}

	async getStuSyllabusDataFromOrigin(courseYear, courseTerm, user) {
		if(!this.hd.zf._checkZfLogin(user))
			return this.reject('-3007', 'zf user not logined in')
		const {userId, session} = user.data.zf
		let cookie = {}
	 	cookie[this.config.zf.url] = {
			'ASP.NET_SessionId': session
		}
		const result = await this.common.reqPost({
			url: `${this.config.zf.url}xskbcx.aspx`,
			cookie,
			headers: {
				'content-type': 'application/x-www-form-urlencoded',
				Referer: `${this.config.zf.url}xs_main.aspx?xh=${userId}`,
				charset: 'UTF-8'
			},
	 		getData: {
	 			xh: userId,
	 			gnmkdm: 'N121603'
	 		},
	 		data: {
	 			__EVENTTARGET: 'xqd',
	 			__EVENTARGUMENT: '',
	 			__VIEWSTATE: this.config.zf.viewState.stuSyllabus,
	 			xnd: courseYear,
	 			xqd: courseTerm
	 		},
	 		getStatus: true
		})
		let {status, data} = result
		console.log(data)
		if(status.code != 200) {
			await this.hd.zf.zfLogout(user)
			return this.reject('-3003', 'zf resources not found')
		}
		let isClassSyllabus = false
		if(/alert\('您本学期课所选学分小于 2分'\)/.test(data)) {
			console.log('student onerself syllabus not found')
			const classResult = await this.common.reqGet({
				url: `${this.config.zf.url}tjkbcx.aspx`,
				cookie,
				headers: {
					'content-type': 'application/x-www-form-urlencoded',
					Referer: `${this.config.zf.url}xs_main.aspx?xh=${userId}`,
					charset: 'UTF-8'
				},
		 		data: {
		 			xh: userId,
		 			gnmkdm: 'N121601'
		 		},
		 		getStatus: true
			})
			let {status:classStatus, data:classData} = classResult
			status = classStatus
			data = classData
			if(status.code != 200) {
				console.log('student class syllabus not found')
				if(user.type != 'student')
					return {}
				return this.reject('-3015', 'this term syllabus not publish')
			}
			isClassSyllabus = true
		}
		const syllabusData = await this._convertStuSyllabusData(data, courseYear, courseTerm, isClassSyllabus)
		if(syllabusData.courseCount == 0) {
			console.log('student syllabus not found', user.type)
			if(user.type != 'student')
				return {}
			return this.reject('-3015', 'this term syllabus not publish')
		}
		console.log(`this student have ${parseInt(syllabusData.courseCount / 2)} pitch course`)
		return syllabusData
	}

	async getTchSyllabusDataFromOrigin(courseYear, courseTerm, user) {
		if(!this.hd.zf._checkZfLogin(user))
			return this.reject('-3007', 'zf user not logined in')
		const {userId, session} = user.data.zf
		let cookie = {}
	 	cookie[this.config.zf.url] = {
			'ASP.NET_SessionId': session
		}
		const result = await this.common.reqPost({
			url: `${this.config.zf.url}jstjkbcx.aspx`,
			cookie,
			headers: {
				'content-type': 'application/x-www-form-urlencoded',
				Referer: `${this.config.zf.url}js_main.aspx?xh=${userId}&zgh=${userId}`,
				charset: 'UTF-8'
			},
	 		getData: {
	 			zgh: userId,
	 			gnmkdm: 'N122303'
	 		},
	 		data: {
	 			__EVENTTARGET: '',
	 			__EVENTARGUMENT: '',
	 			__VIEWSTATE: this.config.zf.viewState.tchSyllabus,
	 			xn: courseYear,
	 			xq: courseTerm,
	 			bm: '',
	 			TextBox1: userId,
	 			js: '0417',
	 			Button2: '%E6%9F%A5%E8%AF%A2%E6%95%99%E5%B8%88'
	 		},
	 		getStatus: true
		})
		const {status, data} = result
		console.log(data)
		if(status.code != 200) {
			console.error('teacher syllabus is empty')
			return {courseCount: 0, courseData: [], courseClasses: []}
		}
		const syllabusData = await this._convertTchSyllabusData(data)
		if(syllabusData.courseCount == 0) {
			console.log('teacher syllabus not found')
			if(user.type != 'teacher')
				return {courseCount: 0, courseData: [], courseClasses: []}
			return this.reject('-3015', 'this term syllabus not publish')
		}
		console.log(`this student have ${parseInt(syllabusData.courseCount / 2)} pitch course`)
		return syllabusData
	}

	convertStuCourseTime(courseTime, weekIndex, pitchIndex) {
		return _convertStuCourseTime(courseTime, weekIndex, pitchIndex)
	}

	//应用调课数据
	async applyCourseChange(syllabusData) {
		return
	}

	convertCourseTch(courseTchStr) {
		return this._convertCourseTch(courseTchStr)
	}

	async _addCorrelSyllabus(stuId, correlStuId) {
		if(!this.hd.student.checkStuId(stuId) || !this.hd.student.checkStuId(correlStuId))
			return this.reject('-2010', 'student id invalid')
		const correlId = await this.crypto.md5(stuId + correlStuId)
		const _correlId = await this.crypto.md5(correlStuId + stuId)
		const correlDate = this.common.timestamp()
		const correlResult = await this.mysql.cxxy.select({
			table: 'syllabus_correl',
			field: 'correlId',
			where: 'correlId=$correlId or correlId=$_correlId',
			data: {
				correlId,
				_correlId: _correlId
			}
		})

		if(correlResult.length == 2)
			return this.reject('-2059', 'syllabus is already correl')

		console.log(correlResult)

		if((!correlResult[0] || correlResult[0].correlId != correlId) && (!correlResult[1] || correlResult[1].correlId != correlId)) {
			await this.mysql.cxxy.insert({
				table: 'syllabus_correl',
				field: [
					'correlId',
					'correlStuId',
					'correlOtherStuId',
					'correlDate'
				],
				data: {
					correlId,
					stuId,
					correlStuId,
					correlDate
				}
			})
		}

		if((!correlResult[0] || correlResult[0].correlId != _correlId) && (!correlResult[1] || correlResult[1].correlId != _correlId)) {
			await this.mysql.cxxy.insert({
				table: 'syllabus_correl',
				field: [
					'correlId',
					'correlStuId',
					'correlOtherStuId',
					'correlDate'
				],
				data: {
					correlId: _correlId,
					stuId: correlStuId,
					correlStuId: stuId,
					correlDate
				}
			})
		}
	}

	async _convertStuSyllabusData(htmlStr, courseYear, courseTerm, isClassSyllabus = false) {
		//存放待生成入库的课程列表
		let courseList = []
		let colorList = []
		let courseData = {}
		let courseCount = 0
		const that = this
		const $ = this.common.parseHTML(htmlStr, false)
		//校验结果是否是请求的课程表
		if(isClassSyllabus ? ($('#xn option[selected="selected"]').val() != courseYear || $('#xq option[selected="selected"]').val() != courseTerm) : ($('#xnd option[selected="selected"]').val() != courseYear || $('#xqd option[selected="selected"]').val() != courseTerm))
			return {
				courseCount,
				courseData
			}
		//遍历本周所有课程项目
		const table = isClassSyllabus ? $('#Table6 tbody tr') : $('#Table1 tbody tr')
		table.each(function(pitchIndex, element) {
			//排除周数和早晨的空部分以及课表空部分的第二行
			if(pitchIndex < 2 || pitchIndex % 2 != 0)
				return
			//遍历本周此节的的所有课程
			$(this).children('td[align="Center"]').each(function(weekIndex) {
				const temp = $(this).html().replace(/<br><font color="red">\(调\d+\)<\/font>/g, '').split('<br>')
				const courseSum = (temp.length % (isClassSyllabus ? 5 : 6)) + 1
				//遍历本节的所有课程
				const pNum = isClassSyllabus ? 6 : 7
				for(let i = 0;i < courseSum * pNum;i += pNum) {
					const [courseName, courseTime, courseTch, courseAddr] = [that.common.trim(temp[i]), that.common.trim(temp[i + 1]), that.common.trim(temp[i + 2]), that.common.trim(temp[i + 3])]
					//判断此课程是否为无课状态
					if(!courseTime) {
						//无课处理
						break
					}
					console.log('课程', courseName, '时间', courseTime, '教师', courseTch, '地点', courseAddr, weekIndex, pitchIndex)
					const courseTimeList = that._convertStuCourseTime(courseTime, weekIndex, pitchIndex)
					for(let time of courseTimeList) {
						const {startWeek, endWeek, doubleWeek, week, day, pitch} = time
						const courseTchList = that._convertCourseTch(courseTch)
						//生成数据
						if(!courseData[day])
							courseData[day] = {}
						for(let p of pitch) {
							if(!courseData[day][p])
								courseData[day][p] = []
							courseData[day][p].push({
								name: courseName,
								color: that._randomCourseColorIndex(courseName + courseAddr, courseList, colorList),
								time: {
									sw: startWeek,
									ew: endWeek,
									dw: doubleWeek
								},
								tchs: courseTchList,
								room: courseAddr
							})
							courseCount++
						}
					}
				}
			})
		})
		return {
			courseCount,
			courseData
		}
	}

	async _convertTchSyllabusData(htmlStr) {
		//存放待生成入库的课程列表
		let courseList = []
		let colorList = []
		let courseData = {}
		let courseClasses = []
		let courseCount = 0
		const that = this
		const $ = this.common.parseHTML(htmlStr, false)
		//遍历本周所有课程项目
		$('#Table6 tbody tr').each(function(pitchIndex, element) {
			//排除周数和早晨的空部分以及课表空部分的第二行
			if(pitchIndex < 2 || pitchIndex % 2 != 0)
				return
			//遍历本周此节的的所有课程
			$(this).children('td[align="Center"]').each(function(weekIndex) {
				const temp = $(this).html().split('<br>')
				const courseSum = (temp.length % 5) + 1
				//遍历本节的所有课程
				for(let i = 0;i < courseSum * 6;i += 6) {
					let [courseName, courseTime, courseTch, courseAddr, courseClass] = [that.common.trim(temp[i]), that.common.trim(temp[i + 1]), that.common.trim(temp[i + 2]), that.common.trim(temp[i + 3]), that.common.trim(temp[i + 4])]
					//判断此课程是否为无课状态
					if(!courseTime) {
						//无课处理
						break
					}
					courseName = courseName.replace(/\[\]$/, '')
					console.log(courseName, courseTime, courseTch, courseAddr, courseClass, weekIndex, pitchIndex)
					const courseTimeList = that._convertTchCourseTime(courseTime, weekIndex, pitchIndex)
					const courseTchList = that._convertCourseTch(courseTch)
					courseClasses.push(courseClass)
					for(let time of courseTimeList) {
						const {startWeek, endWeek, doubleWeek, week, day, pitch} = time
						//生成数据
						if(!courseData[day])
							courseData[day] = {}
						for(let p of pitch) {
							if(!courseData[day][p])
								courseData[day][p] = []
							courseData[day][p].push({
								name: courseName,
								color: that._randomCourseColorIndex(courseName + courseAddr, courseList, colorList),
								time: {
									sw: startWeek,
									ew: endWeek,
									dw: doubleWeek
								},
								tchs: courseTchList,
								room: courseAddr,
								cname: that.common.trim(courseClass)
							})
							courseCount++
						}
					}
				}
			})
		})
		return {
			courseCount,
			courseData,
			courseClasses
		}
	}

	_convertStuCourseTime(courseTime, weekIndex = 0, pitchIndex = 2, reConvert = false) {
		const tempList = courseTime.split(';')
		console.log('转换时间', tempList)
		let courseTimeList = []
		for(let time of tempList) {
			const weeks = ['一', '二', '三', '四', '五', '六', '日']
			const weekSDs = ['单', '双']
			let match, week, pitch, startWeek, endWeek, weekStatus
			time = time.trim()
			try {
				if(time.length == 0) 
					continue
				//匹配字段，星期n，第几节，起始周，结束周，单双周
				[match, week, pitch, startWeek, endWeek, weekStatus] = 
				time.match(/^周(\u4e00|\u4e8c|\u4e09|\u56db|\u4e94|\u516d|\u65e5)第(.+)节\{第(\d{1,2})-(\d{1,2})\周\}$/) || 
				time.match(/^周(\u4e00|\u4e8c|\u4e09|\u56db|\u4e94|\u516d|\u65e5)第(.+)节\{第(\d{1,2})-(\d{1,2})\周\|(单|双)周\}$/) || 
				time.match(/^周(\u4e00|\u4e8c|\u4e09|\u56db|\u4e94|\u516d|\u65e5)第(.+)节\{第(\d{1,2})-(\d{1,2})\周(单|双)周\}$/) || []
				if(!match) {
					console.error('个人课表普通课规则未匹配')
					let pitchNum;
					//处理选修课
					[match, startWeek, endWeek, pitchNum, weekStatus] = 
					time.match(/^\{第(\d{1,2})-(\d{1,2})\周\|?(\d+)节\/周\}$/) || 
					time.match(/^\{第(\d{1,2})-(\d{1,2})\周\|?(\d+)节\/(单|双)周\}$/) || []
					if(!match) {
						console.error('个人课表选修课规则未匹配')
						//处理班级课表
						const result = time.match(/^(\d{1,2})-(\d{1,2})\((\d+,\d+)\)$/) || time.match(/^(\d{1,2})-(\d{1,2})(单|双)\((\d+,\d+)\)$/) || [];
						(result.length == 4 ? [match, startWeek, endWeek, pitch] = result : [match, startWeek, endWeek, weekStatus, pitch] = result)
						if(!match) {
							console.error('班级课表普通课程规则未匹配')
							let weekStr = ''
							//处理超级奇葩的班级课程表
							const _result = time.match(/^(.+)(单|双)\((\d+,\d+)\)$/) || time.match(/^(.+)\((\d+,\d+)\)$/) || [];
							(_result.length == 3 ? [match, weekStr, pitch] = _result : [match, weekStr, weekStatus, pitch] = _result)
							if(!match || reConvert) {
								console.error('班级课表分离周普通课程（单双周）规则未匹配');
								throw this.reject('-3014', 'cannot convert course time', true)
							}
							let timeStr = ''
							let weekTemp = weekStr.split(',')
							console.log('拆分', weekTemp, pitch)
							for(let _week of weekTemp) {
								_week = _week.trim();
								[match, startWeek, endWeek, weekStatus] = _week.match(/^(\d{1,2})$/) || _week.match(/^(\d{1,2})-(\d{1,2})$/) || _week.match(/^(\d{1,2})-(\d{1,2})(单|双)$/) || []
								if(!match)
									throw this.reject('-3014', 'cannot convert course time', true)
								timeStr += `${startWeek}-${endWeek || startWeek}${weekStatus && weekSDs.indexOf(weekStatus) != -1 ? weekStatus : ''}(${pitch});`
							}
							timeStr = this.common.rtrim(timeStr, ';')
							courseTime = timeStr
							console.log('生成再次转换的时间字符串', courseTime)
							//再次转换生成的时间字符串
							return this._convertStuCourseTime(courseTime, weekIndex, pitchIndex, true)
						}
					}
					else {
						const nowPitch = pitchIndex - 1
						pitch = ''
						for(let i = 0;i < pitchNum;i++) {
							pitch += `${nowPitch + i},`
						}
						pitch = this.common.rtrim(pitch, ',')
					}
					week = weeks[weekIndex]
				}
			}
			catch(err) {
				console.error(err)
				throw this.reject('-3014', 'cannot convert course time', true)
			}
			//获得周n的n
			const day = weeks.indexOf(week) + 1
			//检查数据正确性
			if(day == 0)
				throw this.reject('-3010', 'syllabus week data invalid', true)
			if(isNaN(startWeek) || isNaN(endWeek))
				throw this.reject('-3011', 'syllabus pitch or week num data invalid', true)
			if(weekStatus && weekSDs.indexOf(weekStatus) == -1)
				throw this.reject('-3012', 'syllabus week status invalid', true)
			courseTimeList.push({
				startWeek,
				endWeek,
				doubleWeek: weekSDs.indexOf(weekStatus),
				day,
				pitch: pitch.split(',')
			})
		}
		if(courseTimeList.length == 0)
			return []
		console.log(courseTimeList)
		return courseTimeList
	}

	_convertTchCourseTime(courseTime, weekIndex = 0, pitchIndex = 2) {
		const tempList = courseTime.split(';')
		let courseTimeList = []
		for(let time of tempList) {
			const weeks = ['一', '二', '三', '四', '五', '六', '日']
			const weekSDs = ['单', '双']
			let match, date, week, day, pitch, startWeek, endWeek, weekStatus, doubleWeek
			try {
				if(time.trim().length == 0) 
					continue
				//挂上周数
				week = weeks[weekIndex]
				//获得周n的n
				day = weekIndex + 1
				if(day == 0)
					throw this.reject('-3010', 'syllabus week data invalid', true);
				//先分离节数和时间字符串
				[match, date, pitch] = time.match(/^(.+)\((\d{1,2},\d{1,2})\)$/) || []
				if(match) {
					const re = new RegExp(/(\d{1,})(-(\d{1,}))*(单|双)*/g)
					let temp
					while((temp = re.exec(date)) != null) {
						[match, startWeek, , endWeek, weekStatus] = temp
						if(!this.common.isExists(endWeek))
							endWeek = startWeek
						//检查数据正确性
						doubleWeek = weekSDs.indexOf(weekStatus)
						if(isNaN(startWeek) || isNaN(endWeek))
							throw this.reject('-3011', 'syllabus pitch or week num data invalid', true)
						if(weekStatus && doubleWeek == -1)
							throw this.reject('-3012', 'syllabus week status invalid', true)
						courseTimeList.push({
							startWeek,
							endWeek,
							doubleWeek,
							day,
							pitch: pitch.split(',')
						})
					}
				}
				else {
					//匹配部分选修课
					[match, pitchNum, weekStatus, startWeek, endWeek] = time.match(/^(\d{1,2})节\/(单|双)*周\((\d{1,2})-(\d{1,2})\)$/) || []
					if(!match)
						throw this.reject('-3014', 'cannot convert course time', true)
					const nowPitch = pitchIndex - 1
					pitch = ''
					for(let i = 0;i < pitchNum;i++) {
						pitch += `${nowPitch + i},`
					}
					pitch = this.common.rtrim(pitch, ',')
					doubleWeek = weekSDs.indexOf(weekStatus)
					if(isNaN(startWeek) || isNaN(endWeek))
						throw this.reject('-3011', 'syllabus pitch or week num data invalid', true)
					if(weekStatus && doubleWeek == -1)
						throw this.reject('-3012', 'syllabus week status invalid', true)
					courseTimeList.push({
						startWeek,
						endWeek,
						doubleWeek,
						day,
						pitch: pitch.split(',')
					})	
				}
			}
			catch(err) {
				console.error(err)
				throw this.reject('-3014', 'cannot convert course time', true)
			}
		}
		return courseTimeList
	}

	_randomCourseColorIndex(courseStr, courseList, colorList) {
		//判断此课程是否需要生成颜色
		if(courseList.indexOf(courseStr) == -1) {
			//生成颜色index
			const index = parseInt(Math.random() * 35)
			//判断是否已经达到35种颜色不能继续递归
			if(colorList.length == 35)
				return 36
			//判断此颜色是否已经被使用过
			if(colorList.indexOf(index) == -1) {
				courseList.push(courseStr)
				colorList.push(index)
				return index
			}
		}
		else
			return colorList[courseList.indexOf(courseStr)]
		this._randomCourseColorIndex(courseStr, courseList, colorList)
	}

	async _addStuCourseData(stuId, stuSyllabus, courseTime, majorId, classId) {
		if(!stuSyllabus)
			return
		if(!majorId || !classId) {
			const result = await this.mysql.cxxy.select({
				table: [
					'major m',
					'class c',
					'student s'
				],
				field: [
					'm.majorId',
					'c.classId'
				],
				where: 'c.majorId=m.majorId && s.stuClass=c.classId && s.stuId=$stuId',
				data: {
					stuId
				}
			})
			if(result.length == 0)
				return this.reject('-2022', 'sync syllabus add course failed')
			majorId = result[0].majorId
			classId = result[0].classId
		}
		await this.mysql.cxxy.trans()
		await this._addStuSyllabusData(stuId, courseTime, stuSyllabus)
		await this._addCourseData(majorId, classId, stuSyllabus, courseTime)
		await this.mysql.cxxy.cmt()
	}

	async _addTchCourseData(tchId, tchSyllabus, courseTime, courseClasses) {
		if(courseClasses.length == 0 || tchSyllabus.length == 0)
			return
		let classIds = new Set(), majorShortNames = []
		for(let c of courseClasses) {
			const temp = c.match(/^\d+(.{2})\d+班$/)
			if(!temp[1])
				return this.reject('-2068', 'teacher course class name invalid')
			const classId = this.crypto.md5(c)
			if(!classIds.has(classId)) {
				classIds.add(classId)
				majorShortNames.push(temp[1])
			}
		}
		classIds = Array.from(classIds)
		await this.mysql.cxxy.trans()
		await this._addTchSyllabusData(tchId, courseTime, tchSyllabus)
		const majorData = await this.mysql.cxxy.select({
			table: 'major',
			field: [
				'majorId',
				'majorShortName'
			],
			where: `majorShortName in ('${majorShortNames.join(`','`)}') && majorScrap=0`
		})
		let courseIds = []
		for(let index in classIds) {
			const classId = classIds[index]
			const shortName = majorShortNames[index]
			for(let _index in majorData) {
				const {majorId, majorShortName} = majorData[_index]
				console.log(shortName, majorShortName)
				if(shortName == majorShortName) {
					const ids = await this._addCourseData(majorId, classId, tchSyllabus, courseTime)
					courseIds = courseIds.concat(ids)
					break
				}
				if(_index == majorData.length - 1)
					return this.reject('-2064', 'major is not found')
			}
		}
		//return this.reject('-2064', 'major is not found')
		await this._addCourseRelation(tchId, courseIds, courseTime)
		await this.mysql.cxxy.cmt()
	}

	async _addCourseData(majorId, classId, syllabusData, courseTime) {
		if(!syllabusData)
			return
		if(!/^\d{4}-\d{4}$/.test(courseTime.courseYear) || isNaN(courseTime.courseTerm))
			return this.reject('-2065', 'course year or term invalid')
		const {courseYear, courseTerm} = courseTime
		let roomIdMap = {}
		let courseIdMap = {}
		for(let day in syllabusData) {
			for(let pitch in syllabusData[day]) {
				console.log(syllabusData[day][pitch], day, pitch)
				for(let course of syllabusData[day][pitch]) {
					const {name, time, tchs, room, cname} = course
					const {sw, ew, dw} = time
					if(cname) {
						const _classId = this.crypto.md5(cname)
						if(_classId != classId)
							continue
					}
					//添加教室
					const roomId = this.crypto.md5(room)
					roomIdMap[roomId] = room
					for(let tchName of tchs) {
						//添加课程
						const courseId = this.crypto.md5(name + tchName + room + sw + ew + dw + day + pitch + classId)
						courseIdMap[courseId] = { name, day, pitch, majorId, classId, roomId, sw, ew, dw }
					}
				}
			}
		}

		//添加新教室
		const roomIds = Object.keys(roomIdMap)
		const roomResult = await this.mysql.cxxy.select({
			table: 'classroom',
			field: 'roomId',
			where: `roomId in('${roomIds.join(`','`)}')`
		})
		for(let r of roomResult)
			delete roomIdMap[r.roomId]
		let roomData = []
		for(let rId in roomIdMap)
			roomData.push({
				roomId: rId,
				roomName: roomIdMap[rId]
			})
		if(roomData.length > 0) {
			await this.mysql.cxxy.insert({
				table: 'classroom',
				field: [
					'roomId',
					'roomName'
				],
				data: roomData
			})
			console.log('add new classroom', roomData)
		}
		else
			console.log('classroom  already exists')
		
		//添加新课程
		const courseIds = Object.keys(courseIdMap)
		const courseResult = await this.mysql.cxxy.select({
			table: 'course',
			field: 'courseId',
			where: `courseId in('${courseIds.join(`','`)}')`
		})
		for(let c of courseResult)
			delete courseIdMap[c.courseId]
		let _courseData = []
		for(let cId in courseIdMap) {
			const {name, day, pitch, majorId, classId, roomId, sw, ew, dw} = courseIdMap[cId]
			console.log(cId, name, day, pitch, majorId, classId, roomId, sw, ew, dw)
			_courseData.push({
				courseId: cId,
				courseName: name,
				courseDay: day,
				coursePitch: pitch,
				majorId,
				classId,
				roomId,
				startWeek: sw,
				endWeek: ew,
				doubleWeek: dw,
				courseYear,
				courseTerm,
				courseDate: this.common.timestamp()
			})
		}
		if(_courseData.length > 0) {
			await this.mysql.cxxy.insert({
				table: 'course',
				field: [
					'courseId',
					'courseName',
					'courseDay',
					'coursePitch',
					'majorId',
					'classId',
					'roomId',
					'startWeek',
					'endWeek',
					'doubleWeek',
					'courseYear',
					'courseTerm',
					'courseDate'
				],
				data: _courseData
			})
			console.log('add new course', _courseData)
		}
		else
			console.log('course  already exists')
		return courseIds
	}

	async _addCourseRelation(tchId, courseIds) {
		if(courseIds.length == 0)
			return this.reject('-2069', 'course id not found')
		let relationIds = []
		let relationData = []
		for(let courseId of courseIds) {
			const relationId = this.crypto.md5(courseId + tchId)
			relationIds.push(relationId)
			relationData.push({
				relationId,
				tchId,
				courseId,
				relationDate: this.common.timestamp()
			})
		}
		if(relationData.length == 0)
			return []
		const result = await this.mysql.cxxy.select({
			table: 'course_relation',
			field: 'relationId',
			where: `relationId in ('${relationIds.join(`','`)}')`
		})
		for(let {relationId: id} of result) {
			const index = relationIds.indexOf(id)
			if(index != -1) {
				relationIds.splice(index, 1)
				relationData.splice(index, 1)
				console.log(`course relation ${id} already exists`)
			}
		}
		if(relationData.length == 0)
			return []
		await this.mysql.cxxy.insert({
			table: 'course_relation',
			field: [
				'relationId',
				'tchId',
				'courseId',
				'relationDate'
			],
			data: relationData
		})
		return relationIds
	}

	async _addStuSyllabusData(stuId, courseTime, courseData) {
		if(!/^\d{4}-\d{4}$/.test(courseTime.courseYear) || isNaN(courseTime.courseTerm))
			return this.reject('-2065', 'course year or term invalid')
		const {courseYear, courseTerm} = courseTime
		const syllabusId = this.crypto.md5(courseYear + courseTerm + stuId)
		const syllabusData = JSON.stringify(courseData)
		const result = await this.mysql.cxxy.select({
			table: 'student_syllabus',
			field: 'id',
			where: 'syllabusId=$syllabusId',
			data: {
				syllabusId
			}
		})
		if(result.length == 0) {
			console.log(`add student ${stuId} syllabus`)
			await this.mysql.cxxy.insert({
				table: 'student_syllabus',
				field: [
					'syllabusId',
					'syllabusStuId',
					'syllabusData',
					'syllabusYear',
					'syllabusTerm',
					'syllabusDate'
				],
				data: {
					syllabusId,
					syllabusStuId: stuId,
					syllabusData,
					syllabusYear: courseYear,
					syllabusTerm: courseTerm,
					syllabusDate: this.common.timestamp()
				}
			})
		}
		else {
			console.log(`update student ${stuId} syllabus`)
			await this.mysql.cxxy.update({
				table: 'student_syllabus',
				field: [
					'syllabusData=$syllabusData',
					'syllabusDate=$syllabusDate'
				],
				where: 'syllabusId=$syllabusId',
				data: {
					syllabusData,
					syllabusDate: this.common.timestamp(),
					syllabusId
				}
			})
		}
	}

	async _addTchSyllabusData(tchId, courseTime, courseData) {
		if(!/^\d{4}-\d{4}$/.test(courseTime.courseYear) || isNaN(courseTime.courseTerm))
			return this.reject('-2065', 'course year or term invalid')
		const {courseYear, courseTerm} = courseTime
		const syllabusId = this.crypto.md5(courseYear + courseTerm + tchId)
		const syllabusData = JSON.stringify(courseData)
		const result = await this.mysql.cxxy.select({
			table: 'teacher_syllabus',
			field: 'id',
			where: 'syllabusId=$syllabusId',
			data: {
				syllabusId
			}
		})
		if(result.length == 0) {
			console.log(`add teacher ${tchId} syllabus`)
			await this.mysql.cxxy.insert({
				table: 'teacher_syllabus',
				field: [
					'syllabusId',
					'syllabusTchId',
					'syllabusData',
					'syllabusYear',
					'syllabusTerm',
					'syllabusDate'
				],
				data: {
					syllabusId,
					syllabusTchId: tchId,
					syllabusData,
					syllabusYear: courseYear,
					syllabusTerm: courseTerm,
					syllabusDate: this.common.timestamp()
				}
			})
		}
		else {
			console.log(`update teacher ${tchId} syllabus`)
			await this.mysql.cxxy.update({
				table: 'teacher_syllabus',
				field: [
					'syllabusData=$syllabusData',
					'syllabusDate=$syllabusDate'
				],
				where: 'syllabusId=$syllabusId',
				data: {
					syllabusData,
					syllabusDate: this.common.timestamp(),
					syllabusId
				}
			})
		}
	}

	async _checkCorrelSyllabus(stuId, correlStuId) {
		const data = await this.mysql.cxxy.select({
			table: 'student_syllabus',
			field: 'syllabusStuId',
			where: 'syllabusStuId=$syllabusStuId',
			data: {
				syllabusStuId: correlStuId
			}
		})
		if(!data[0])
			return this.reject('-2046', 'correl student not found')
		const correlData = await this.mysql.cxxy.select({
			table: 'syllabus_correl',
			field: 'correlId',
			where: 'correlStuId=$correlStuId && correlOtherStuId=$correlOtherStuId',
			data: {
				correlStuId: stuId,
				correlOtherStuId: correlStuId
			}
		})
		if(!correlData[0])
			return false
		return true
	}

	_convertCourseTch(courseTchStr) {
		//获得此课程教师们
		const courseTchTemp = courseTchStr.split('/')
		if(courseTchTemp.length == 0)
			throw this.reject('-3023', 'course teacher not found')
		return courseTchTemp
	}

}

module.exports = h_syllabus
